//本章重点：（1）数据类型详细介绍  （2）整型在内存中的存储：源码、反码、补码  （3）大小端字节序介绍及判断   （4）浮点型在内存中的存储解析

//1.数据类型介绍
//c语言类型：（1）内置类型：int char double之类的  （2）自定义类型，即构造类型
//
//类型的意义：（1）使用这个类型开辟内存空间的大小 （2）如何看待内存空间的视角：也就是int和float同样定义了一个10.但是在内存中存储的方式并不相同

//整型家族：
//(1)char: unsigned char>>>0-255       signed char>>> -128--127          -----  -128的
//(2)short: unsigned short实际上等同于unsigned short int        signed short实际上等同于signed short int        只是平时可以省略不写
//(3)long: unsigned long实际上等同于unsigned long int        signed long实际上等同于signed long int

//浮点型家族:
//(1)float
//(2)double

//构造类型：
//（1）数组类型
//（2）结构体类型struct
//（3）枚举类型enum
//（4）联合类型union

//指针类型：不管什么种类，各种指针大小都是4（8）个字节，32位平台（64位平台）
// （1）int*
// （2）char*
// （3）float*
// （4）void*

//空类型：也就是void





//2.整型在内存中的存储
//一个变量的创建是要在内存中开辟内存空间的，空间大小是根据不同类型决定的
//四个二进制位转换为一个16进制位           -----一个字节是8个比特位，也就是00000000为一个字节，也就是两个16进制位代表一个字节 ， a7(17)
//在计算机系统中，数值一律用补码来表示存储。原因在于，使用补码，可以将符号位和数值域统一处理；同时，加法和减法也可以统一处理（cpu只有加法器,乘除也是转换成加法）
// 此外，补码与与源码相互，转换，其运算过程也是相同的，不需要额外的硬件电路

//补码在相加的过程中，如果超过了32位，那么会将左边的二进制位舍去>>>10000001舍去位为0000001

//变量存放的数字在内存视图中是以16进制的形式显示出来的，比如3显示的时候是  03 00 00 00  ，每两位占用一个字节，二进制是00000011，有效数字为是先从低位开始的
//以16进制形式在内存视图中显示出来的时候会出现有效数字位在前面的情况则和大小端存储的方式有关
//这样的存储方式叫做小端存储模式





//2-1.大端小端（大端/小端 字节序存储模式）             （也就是千位>百位>十位  千位就是高地址）
//（1） 
// 例如：十六进制数字 11 22 33 44>>>这就是正常的序列  
//首先，16进制的每两个数字都能用8个比特位来表示，    二进制 1111 就是数字15。因此刚好一个字节就是16进制数字的两位
//至于说是从高地址存放11，还是从低地址存放11，这就和大小端字节序有关系
//但是不管内存中是怎么进行存放的，后面的地址始终比前面的地址高    11 22 33 44     44 33 22 11      这两种字节序始终是低地址到高地址进行排列的


//（2）
// 大端（存储）模式：数据的低位保存在内存的高地址中，而数据的高位保存在内存的低地址中。    上数字存储的形式就是 11 22 33 44        -----大==高==高地址开始存储
// 小端（存储）模式：数据的低位保存在内存的低地址中，而数据的高位保存在内存的高地址中。    上数字存储的形式就是 44 33 22 11        -----小==低==低地址开始存储

//在内存的监视器显示的上面。显示出来的地址后面的存储的16进制数字，其存放位置是低地址到高地址
//十六进制在存储的时候屏幕上显示的是 01 00 00 00  01是低位

//(3)
//int a=1;char *p=&a;*p=1;说明char*是先从内存中的低地址位进行保存的。      -----char* p，这个指针指向的是内存中的低地址，但是低地址中会保存数字的高位还是低位就和大小端字节序存储模式有关了
//  char a=-1;signed char a=-1;unsigned char a=-1;  a的值分别为-1 -1 255.第二个输出的时候因为有符号要根据补码算出源码的值进行输出。第三个无符号位
//这种办法就是利用指针的类型来进行截取，因为指针指向的都是变量所占内存的最低处，所以截取看是还是0就能进行判断

//大小段端字节序指的是数据在电脑上存储的字节顺序

//接下来我们用一段代码查看这某个数字在内存中存放的情况
// 





//3.一些实际应用题目
// char 类型的字节循环      127       -128
// 00000000->00000001->01111111->10000000->11111111->00000000                -----从左到右一直再加一，反方向减一效果相同。  并且请注意这些是补码，因为
// #include<stdio.h>
// int main(void)
// {
//     int i=-20; 
//     unsigned int j=10; 
//     printf("%d",i+j);//%d输出的是十进制有符号整型数字，这两个的补码计算完毕后，还得计算相应的源码进行输出
// }

// #include<stdio.h>
// #include<string.h>
// int main(void)
// {
//     char a[1000];//注意，C语言中，什么类型定义的数组，其单个元素就只占用多少类型字节  char就占用1个
//     int i;
//     for(i=0;i<1000;i++)
//     {
//         a[i]=-1-i;
//     }
//     printf("%d\n",strlen(a));//遇见数组中的0就会停止，但是不会将该数字统计进去
//     return 0;
// }








//4.IEEE754规定              -----这个规定是针对二进制浮点数的
// （1）
// 任意一个二进制浮点数可以表示为成下面的形式：(IEEE754规定)     >>>可以理解为二进制的科学计数法
// (-1)^s   *   m   *   2^e      （-1）的s次方，乘以m，乘以(2的e次方)                    -----（-1）^s   这个是表示浮点数的正负的，取0整体则为1（正数），取1整体则为-1（负数）。
// (-1)表示符号位，当s=0，浮点数是正数；    当s=1，浮点数是负数1      这个规则和整数的正负的规则是一样的
// m表示有效数字，1<=m<2       >>>>>>>>>>也就是m只能是1.xxxxxx，后面的 xxx 全是有效位数字
// 2^e表示指数位      此处的e只是代表m有效数字后的小数点有多少位       -----此处的e也就是xxxxx的个数
// 例如9.0在二进制中>>>1001.0>>>二进制的科学计数法是：1.001*2^3  （其实在内存中只保存了001）      这里的乘2和前面的位运算符中的>> <<  介绍的效果是一样的
//就是二进制用科学计数法表示的一种情况


//（2）
//IEEE754规定：在float变量中。s占用1bit，m占用23bit,e占用8bit        >>>4个字节32位
// 
//IEEE754规定：在double变量中，s占用1bit，m占用52bit，e占用11bit      >>>>8个字节64位



//（3）
//IEEE754规定：在计算机内部保存m时，默认这个数的第一位总是1，因此可以被舍去，只保存后面的xxxx部分，比如在保存1.01的时候，只保存01，等到读取的时候，再把第1
//位的1加上去。这样可以节省一位有效数字。以float为例，可以保存24位有效数字

//(4)小数部分转换成二进制的方法
//小数部分转换为二进制就是将小数部分乘2进行取整.例如十进制数字0.725>>>0.725*2=1.45取1>>>0.45*2=0.9取0>>>0.9*2=1.8取1....直到*2=1或者碰见循环小数
// 才停止




//4.1 对于小数是负数的情况
//对于指数e：e是一个无符号整数（unsigned int），在这意味着，如果e为8位，它的取值范围是0-255；如果e为11位，它的取值范围是0-2047.但是我们知道，科学计数法中
// 是可以出现负数的。
// 因此IEEE754规定：存入内存时e的真实值必须要加上一个中间数，对于8位的e，这个中间数是127；    对于11位的e，这个中间数是1023.比如2^10的e是10，所以保存成
// 32位浮点数时，必须保存成10+127=137，即10001001         （也就是说e大于127的，存的是大于1的数。       e小于127的，存的是小于1的数      当然e为127存的就是1）
//不论e是正数还是负数，内存中e的值就是e+127或者e+1023   在于定义的类型是什么
// 
//还有一点我们要明白，1.xxxx   这些x指的是整数部分转换成二进制时后面小数的个数    比如5就是00000101--1.01    此时e取2       5.5--00000101.1--1.011--此时e还取2


// #include<stdio.h>
// int main(void)
// {
//     float f=5.5;//101.1>>>IEEE754存放的方式(-1)^0*1.011*2^2  s=0  m=1.01   e=2      这都是保存到32个比特位中的，如下
//     s：一位0     e：2+127转换为二进制数字：10000001（不够八位的在前面补零）     m：01100000000000000000000    m能存放的只有23或52位，只是m（1.xxx）只保存x这些有效数字
//      所以该数字在内存中的存储就是0100 0000 1011 0000 0000 0000 0000 0000这个32位的二进制数字       -----高位存符号，再存指数个数（e+127/1023）,再存xxxx，不够位数补零
//      在内存中16进制的形式就是:  40   b0   00   00   00   00   00   00(小端存储模式可能要倒着进行存储)
//     return 0;
// }








//上面4.的精简解释版本                        -----这样的存储方式就是浮点数在内存中的存储方式，浮点数没有原码反码补码的概念
//小数怎么转换成二进制并且存储
//floot变量：占用4个字节，32位。
// （1）高地址第一位还是符号位0或1      占用1位
// （2）将这个小数转换成二进制，再用类似于十进制的科学计数法表示;例如5.5      00000101.1  -----1.011*2^2
// （3）将这个指数+127，再转换成二进制，接着上面的符号位进行存放     占用8位
// （4）最后只取小数点后的有效数字，也就是011，后面补零               占用23位
// （5）共计32位

//double变量：占用8个字节，64位。
// （1）高地址第一位还是符号位0或1      占用1位
// （2）将这个小数转换成二进制，再用类似于十进制的科学计数法表示;例如5.5      00000101.1  -----1.011*2^2
// （3）将这个指数+1023，再转换成二进制，接着上面的符号位进行存放      占用11位
// （4）最后只取小数点后的有效数字，也就是011，后面补零               占用52位
// （5）共计64位

//此外请注意，转换成二进制在内存中是大小端字节序存储的问题。
//符号位（1位）别名：数符         指数位（8/11位）别名：阶码            数字位（23/52位）：尾数






//4.3 
// IEEE754的指数e在内存中取出还可以分为三种情况                          -------也就是将小数的二进制转换成十进制的小数
//                    -----知道这个小数的二进制，怎么将其转换成十进制的小数，其中e的取值情况
// （1）e不全为0或者不全为1：这时，浮点数就采用下面的表示                -----最常见的情况
//指数e的值减去127（或者1023），得到真实值，再将有效数字m前加上第一位的1.比如：0.5的二进制形式是0.1，由于规定正数部分必须为1，即将小数点右移一位，也就是：
//1.0*2^(-1)，在内存中存储的是-1+127的二进制位01111110，而尾数1.0去掉整数部分为0.补齐0到23位 0 01111110 00000000000000000000000

// （2）e全为0：             ------
//浮点数的指数e真实值等于1-127（或1-1023），即为真实值，有效数字m不再加上第一位的1，而是还原为0.xxxxxxx的小数。这样做是为了表示+-0，以及接近于0的很小的数字
//0 00000000 01100000000000000000000000     换成规定的形式就是（-1）^0*0.011*2^(-126)    代表正负无穷小或者0

// (3)e全为1：
// 这时，有效数字m全为0，表示+-无穷大，(正负取决于符号位s)
// 0 11111111 01100000000000000000000     代表无穷大 1*2^126







//补充：科学计数法
// 例如:178232300000科学计数法>>>1.782323*10^11>>>>>在计算机中表示为:1.782323e11     -----e后面的是指数，当然也能为负数
//小数表示：2.34345>>>>24345*10^-5>>>>>在计算机中


 //#include<stdio.h>
 //int main(void)
 //{
 //    int n=9;
 //    float* p_float=(float*)&n;//这个指针指向了整型数字n的地址，但是并不会改变n在内存中的补码，仅仅是这个指针变量在进行解引用的时候是以浮点数的读取方式读取这个原码
 //    printf("n的值为:%d\n",n);//以整型格式打印出d的值，n的值并没有改变，因为没有对n进行强制类型转换
 //    printf("*p_float的值为:%f\n",*p_float);//对存放整型的浮点型指针进行解引用1
 //    *p_float=9.0;
 //    printf("num的值为:%d\n",n);//通过浮点型指针进行解引用赋值
 //    printf("*p_float的值为:%f\n",*p_float);//通过解引用赋值，再将赋值打印出去
 //}